package org.leon;

import org.leon.pojo.Configuration;
import org.leon.pojo.MapperStatement;
import org.leon.utils.BoundSql;
import org.leon.utils.GenericTokenParser;
import org.leon.utils.ParameterMapping;
import org.leon.utils.ParameterMappingTokenHandler;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by leon.L on 2019/12/25.
 * Desc :
 */
public class SimpleExecutor implements Executor {

    @Override
    public <E> List<E> query(Configuration configuration, MapperStatement mapperStatement, Object... params) throws Exception {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        List<Object> rList = new ArrayList<>();

        try {
            connection = configuration.getDataSource().getConnection();
            String sql = mapperStatement.getSql();

            // 转化带有#{}的sql为JDBC的？占位符的sql
            BoundSql boundSql = parseSql(sql);

            preparedStatement = connection.prepareStatement(boundSql.getJdbcSql());
            Class<?> paramClass = getClassType(mapperStatement.getParamterType());
            // 设置参数
            List<ParameterMapping> parameterMappingList = boundSql.getParameterMappingList();
            for (int i = 0; i < parameterMappingList.size(); i++) {
                ParameterMapping parameterMapping = parameterMappingList.get(i);
                String fieldName = parameterMapping.getContent();

                Field paramField = paramClass.getDeclaredField(fieldName);
                paramField.setAccessible(true);

                Object o = paramField.get(params[0]);

                preparedStatement.setObject(i + 1, o);
            }

            // 执行sql
            ResultSet resultSet = preparedStatement.executeQuery();

            // 封装结果集
            Class<?> resultTypeClass = getClassType(mapperStatement.getResultType());       // 获取返回值类型class
            Constructor<?> declaredConstructor = resultTypeClass.getDeclaredConstructor();  // 获取类型的空构造函数对象

            while (resultSet.next()) {
                Object o = declaredConstructor.newInstance();   // 根据构造函数对象获得一个实例
                // 元数据，主要包含返回结果的属性名称(数据库字段名称)
                ResultSetMetaData metaData = resultSet.getMetaData();
                for (int i = 1; i <= metaData.getColumnCount(); i++) {
                    // 字段名称
                    String columnName = metaData.getColumnName(i);
                    // 根据字段名称获取结果集中的值
                    Object value = resultSet.getObject(columnName);

                    PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName, resultTypeClass);
                    Method writeMethod = propertyDescriptor.getWriteMethod();
                    writeMethod.invoke(o, value);
                }
                rList.add(o);
            }
        }catch (Exception e){
            throw e;
        }finally {
            if (preparedStatement != null) {
                preparedStatement.close();
            }
            if (connection != null) {
                connection.close();
            }
        }

        return (List<E>) rList;
    }

    @Override
    public int update(Configuration configuration, MapperStatement mapperStatement, Object param) throws Exception {
        Connection connection = null;
        PreparedStatement preparedStatement = null;

        int rows ;
        try {
            connection = configuration.getDataSource().getConnection();
            String sql = mapperStatement.getSql();

            BoundSql boundSql = parseSql(sql);

            preparedStatement = connection.prepareStatement(boundSql.getJdbcSql());
            Class<?> paramClass = getClassType(mapperStatement.getParamterType());

            // 设置参数
            List<ParameterMapping> parameterMappingList = boundSql.getParameterMappingList();
            for (int i = 0; i < parameterMappingList.size(); i++) {
                ParameterMapping parameterMapping = parameterMappingList.get(i);
                String fieldName = parameterMapping.getContent();

                Field paramField = paramClass.getDeclaredField(fieldName);
                paramField.setAccessible(true);

                Object o = paramField.get(paramClass.cast(param));

                preparedStatement.setObject(i + 1, o);
            }

            preparedStatement.execute();
            rows = preparedStatement.getUpdateCount();
            // connection.commit();   // 已经指定了 autocommit=true
        } catch (Exception e) {
            throw e;
        }finally {
            if (preparedStatement != null) {
                preparedStatement.close();
            }
            if (connection != null) {
                connection.close();
            }
        }

        return rows;
    }

    private Class<?> getClassType(String className) throws ClassNotFoundException {
        if (className != null && !className.isBlank()) {
            return Class.forName(className);
        }
        return null;
    }


    private BoundSql parseSql(String sql) {
        ParameterMappingTokenHandler parameterMappingTokenHandler = new ParameterMappingTokenHandler();

        GenericTokenParser genericTokenParser = new GenericTokenParser("#{", "}", parameterMappingTokenHandler);
        // 解析出的sql
        String parseSql = genericTokenParser.parse(sql);
        // 解析出的#{}中的名称
        List<ParameterMapping> list = parameterMappingTokenHandler.getParameterMappings();

        return new BoundSql(parseSql, list);
    }
}
